package com.n9.qq.android;

import com.n9.qq.android.RegisterProxySvcPack.SvcResponsePbPullGroupMsgProxy;
import com.n9.qq.android.firendlist.GetTroopListReqV2;
import com.n9.qq.android.firendlist.GetTroopListRespV2;
import com.n9.qq.android.firendlist.stTroopNum;
import com.n9.qq.android.mock.MsgListener;
import com.n9.qq.android.mock.OfflineListener;
import com.n9.qq.android.msg_comm.Msg;
import com.n9.qq.android.msg_comm.UinPairMsg;
import com.n9.qq.android.msgsvc.PbGetDiscussMsgResp;
import com.n9.qq.android.msgsvc.PbGetMsgReq;
import com.n9.qq.android.msgsvc.PbGetMsgResp;
import com.n9.qq.android.msgsvc.PbPushMsg;
import com.n9.qq.android.push.PushReq;
import com.n9.qq.android.push.PushResp;
import com.n9.qq.client.AbstractClient;
import com.n9.qq.client.FromServiceMsg;
import com.n9.qq.util.Util;
import com.n9.qq.util.ZipUtils;
import com.qq.jce.wup.UniPacket;
import com.tencent.mobileqq.pb.ByteStringMicro;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * androidqq mock client
 * @author ice
 * @since 2019/1/31 18:22
 */
public class AqClient extends AbstractClient {
    private static final Logger log = LoggerFactory.getLogger(AqClient.class);
    private MsgListener msgListener;

    /**
     * @param hostname 主机名
     * @param port     端口
     */
    public AqClient(String hostname, int port, MsgListener msgListener) {
        super(hostname, port, new AqProtocol());
        this.msgListener = msgListener;
    }

    @Override
    public void handle(FromServiceMsg fromServiceMsg) {
        if (fromServiceMsg instanceof AqFromServiceMsg) {
            AqFromServiceMsg fromMsg = (AqFromServiceMsg) fromServiceMsg;
            RespHead respHead = fromMsg.getRespHead();
            if (Objects.nonNull(respHead)) {
                log.info("服务器主动发包:{}\t{}\t{}", respHead.getServiceCmd(), respHead.getReqId(), Util.convertByteToHexString(fromMsg.getBodyData()));

                String serviceCmd = respHead.getServiceCmd();
                switch (serviceCmd) {
                    case "ConfigPushSvc.PushReq":
                        handlePushReq(fromMsg);
                        break;
                    case "friendlist.GetTroopListReqV2":
                        handleGetTroopListReq(fromMsg);
                        break;
                    case "OnlinePush.PbPushGroupMsg":
                        handlePushGroupMsg(fromMsg);
                        break;
                    case "RegPrxySvc.PbGetGroupMsg":
                        handleOfflientMsg(fromMsg);
                        break;
                    case "MessageSvc.PushNotify":
                        handlePushNotify(fromMsg);
                        break;
                    case "MessageSvc.PbGetMsg":
                        handleGetMsg(fromMsg);
                        break;
                    case "MessageSvc.PushForceOffline":
                        handleClientOffline();
                        break;
                    case "StatSvc.ReqMSFOffline":
                        handleClientOffline();
                        break;
                    default:
                        break;
                }
            } else {
                log.info("server auto send unknow msg：{}", Util.convertByteToHexString(fromMsg.getBodyData()));
            }
        }
    }

    /**
     * 被服务器踢下线
     */
    public void handleClientOffline() {
        context.setStatus(Status.OFFLINE);
        OfflineListener offlineListener = context.getOfflineListener();
        if (Objects.nonNull(offlineListener)) {
            offlineListener.offline(this);
        }
    }

    /**
     * 处理接收消息
     * @param fromMsg
     */
    private void handleGetMsg(AqFromServiceMsg fromMsg) {
        byte[] bytes = Arrays.copyOfRange(fromMsg.getBodyData(), 4, fromMsg.getBodyData().length);

        try {
            if (isZipData(bytes)) {
                bytes = ZipUtils.inflate(bytes);
            }

            PbGetMsgResp pbGetMsgResp = new PbGetMsgResp();
            pbGetMsgResp.mergeFrom(bytes);

            byte[] syncCookie = pbGetMsgResp.sync_cookie.get().toByteArray();
            if (Objects.nonNull(syncCookie) && syncCookie.length > 0) {
                this.context.setvCookies(syncCookie);
            }

            List list = pbGetMsgResp.uin_pair_msgs.get();
            list.forEach(o -> {
                UinPairMsg upm = (UinPairMsg) o;
                upm.msg.get().forEach(m ->handleMsg((Msg)m));
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理通知消息
     * @param fromMsg
     */
    private void handlePushNotify(AqFromServiceMsg fromMsg) {
        final long qqUin = this.getContext().getQqUin();
        final int requestId = this.context.incRequestId();
        byte[] vCookies = this.context.getvCookies();

        PbGetMsgReq pbGetMsgReq = new PbGetMsgReq();
        if (Objects.nonNull(vCookies)) {
            pbGetMsgReq.sync_cookie.set(ByteStringMicro.copyFrom(vCookies));
        }
        pbGetMsgReq.sync_flag.set(0);
        pbGetMsgReq.ramble_flag.set(0);
        pbGetMsgReq.latest_ramble_number.set(0x14);
        pbGetMsgReq.other_ramble_number.set(0x03);
        pbGetMsgReq.online_sync_flag.set(0x01);
        pbGetMsgReq.whisper_session_id.set(0x01);

        byte[] getMsgReqBytes = pbGetMsgReq.toByteArray();

        byte[] body = new byte[getMsgReqBytes.length + 4];
        Util.int32_to_buf(body, 0, body.length);
        System.arraycopy(getMsgReqBytes, 0, body, 4, getMsgReqBytes.length);

        ReqHead reqHead = new ReqHead("MessageSvc.PbGetMsg", this.context.getCookie());
        AqToServiceMsg toServiceMsg = AqToServiceMsg.create0901(reqHead, body, this.context);
        send(toServiceMsg);
    }

    /**
     * 处理群离线消息
     * @param fromMsg
     */
    private void handleOfflientMsg(AqFromServiceMsg fromMsg) {
        byte[] bytes = Arrays.copyOfRange(fromMsg.getBodyData(), 4, fromMsg.getBodyData().length);;
        try {
            //zip压缩标记
            if (isZipData(bytes)) {
                bytes = ZipUtils.inflate(bytes);
            }

            SvcResponsePbPullGroupMsgProxy gmp = new SvcResponsePbPullGroupMsgProxy();
            gmp = (SvcResponsePbPullGroupMsgProxy) gmp.mergeFrom(bytes);

            byte[] msgContent = gmp.msg_content.get().toByteArray();
            byte[] data = new byte[msgContent.length - 4];
            System.arraycopy(msgContent, 4, data, 0, data.length);

            PbGetDiscussMsgResp resp = new PbGetDiscussMsgResp();
            resp = (PbGetDiscussMsgResp) resp.mergeFrom(data);

            List list = resp.msg.get();

            list.forEach(o->{
                Msg msg = (Msg) o;
                handleMsg(msg);
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 判断是否为压缩数据
     * @param bytes
     * @return
     */
    private boolean isZipData(byte[] bytes) {
        if (Objects.isNull(bytes) || bytes.length == 0) {
            return false;
        }
        return bytes[0] == 0x78;
    }

    /**
     * 处理接收到的消息
     * @param msg
     */
    private void handleMsg(Msg msg) {
        if (Objects.nonNull(msg)) {
            msgListener.onReceived(msg, this.context);
        } else {
            log.info("msg listener is null");
        }
    }

    /**
     * 处理接收群消息
     * @param fromMsg
     */
    private void handlePushGroupMsg(AqFromServiceMsg fromMsg) {
        byte[] bytes =  Arrays.copyOfRange(fromMsg.getBodyData(), 4, fromMsg.getBodyData().length);;

        PbPushMsg req = new PbPushMsg();
        try {
            //zip压缩标记
            if (isZipData(bytes)) {
                bytes = ZipUtils.inflate(bytes);
            }
            req.mergeFrom(bytes);
            handleMsg(req.msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析群列表
     * @param fromMsg
     */
    public void handleGetTroopListReq(AqFromServiceMsg fromMsg) {
        byte[] zipData = Arrays.copyOfRange(fromMsg.getBodyData(), 4, fromMsg.getBodyData().length);;
        byte[] inflateData = zipData;
        if (isZipData(zipData)) {
            inflateData = ZipUtils.inflate(zipData);
        }
        byte[] fullByste = new byte[inflateData.length + 4];
        Util.int32_to_buf(fullByste, 0 , fullByste.length);
        System.arraycopy(inflateData, 0, fullByste, 4, inflateData.length);
        UniPacket uniPacket = new UniPacket();
        uniPacket.setEncodeName("UTF-8");
        uniPacket.useVersion3();
        uniPacket.decode(fullByste);


        GetTroopListRespV2 getTroopListRespV2 = uniPacket.get("GetTroopListRespV2", new GetTroopListRespV2(), null);
        log.info("获取群列表成功,群数量:" + getTroopListRespV2.troopcount + "\t" + Util.convertByteToHexString(getTroopListRespV2.vecCookies));
        if (Objects.nonNull(getTroopListRespV2)) {
            getTroopListRespV2.vecTroopList.forEach((o) -> {
                stTroopNum troopNum = (stTroopNum)o;
                log.info("群号:{} \t,群名称:{}", troopNum.GroupUin, troopNum.strGroupName);
                context.addTroopNum(troopNum);
            });
        }

        //单个数据包最多获取72个群,多次请求群列表
//        if (context.getVecTroopList().size() < getTroopListRespV2.troopcount) {
//            requestTroopList(getTroopListRespV2.vecCookies);
//        }
    }


    /**
     * 请求群列表
     * @param vecCookies
     */
    private void requestTroopList(byte[] vecCookies) {
        GetTroopListReqV2 troopListReqV2 = new GetTroopListReqV2();
        troopListReqV2.uin = context.getQqUin();
        troopListReqV2.shVersion = 5;
        troopListReqV2.bGroupFlagExt = 1;
        troopListReqV2.vecCookies = vecCookies;

        UniPacket up = new UniPacket();
        up.useVersion3();
        up.setFuncName("GetTroopListReqV2");
        up.setServantName("mqq.IMService.FriendListServiceServantObj");
        up.put("GetTroopListReqV2", troopListReqV2);
        byte[] buf = up.encode();

        ReqHead reqHead = new ReqHead("friendlist.GetTroopListReqV2", new byte[0]);
        AqToServiceMsg toServiceMsg = AqToServiceMsg.create0901(reqHead, buf, context);

        AqFromServiceMsg send = send(toServiceMsg, 10000);
        if (Objects.isNull(send)) {
            log.info("获取群列表超时");
        } else {
            handleGetTroopListReq(send);
        }
    }

    /**
     * 服务器主动发包,需要回复主动回复的
     * 处理pushReq消息
     * @param fromMsg
     */
    private void handlePushReq(AqFromServiceMsg fromMsg) {
        UniPacket up = new UniPacket(true);
        up.setEncodeName("utf-8");
        up.decode(fromMsg.getBodyData());
        PushReq pushReq = up.getByClass("PushReq", new PushReq());

        PushResp pushResp = new PushResp(pushReq.getType(), pushReq.getlSeq(), null);
        up = new UniPacket(true);
        up.setEncodeName("utf-8");
        up.setServantName("QQService.ConfigPushSvc.MainServant");
        up.setFuncName("PushResp");
        up.put("PushResp", pushResp);

        ReqHead reqHead = new ReqHead("ConfigPushSvc.PushResp", context.getCookie());
        byte[] body = up.encode();
        int requestId = fromMsg.getRespHead().getReqId();
        AqToServiceMsg toServiceMsg = AqToServiceMsg.create0901(reqHead, body, context, requestId);
        send(toServiceMsg);
    }
}
